﻿#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Gezel.Model.Errors;

#endregion

namespace Weazel.Gezel.Model
{
  public partial class Signal
  {
    public void Verify(ref List<VerificationError> errors, ref List<VerificationWarning> warnings, params object[] arguments)
    {
      // check that the name of this signal is ok 
      Identifier.Verify(ref errors, ref warnings, localName, StartPosition);

      // check if signal connects to a child datapath
      if (parent is Datapath)
      {
        Datapath dp = parent as Datapath;

        foreach (string childdpName in dp.ChildDatapathRefs.Keys)
        {
          string[] connections = 
            dp.ChildDatapathRefs[childdpName];

          for (int i = 0; i < connections.Length; i++)
          {
            string connection = connections[i];

            if (connection == this.localName)
            {
              Block childDp;

              if (Model.Datapaths.ContainsKey(childdpName))
              {
                childDp = Model.Datapaths[childdpName];
              }
              else if (Model.IPBlocks.ContainsKey(childdpName))
              {
                childDp = Model.IPBlocks[childdpName];
              }
              else 
              {
                ErrorMessage message = new ErrorMessage(
                    string.Format("Signal '{0}' bound to non-existing datapath or IP-block '{1}'", Name, childdpName)
                );

                VerificationError error = 
                    new VerificationError(
                        VerificationErrorType.NoSuchDatapathOrIpBlock, 
                        message
                    );
              
                errors.Add(error);
                return;
              }
              
              Port port = childDp.GetPortAtPosition(i);

              if (port == null)
              {
                ErrorMessage message = new ErrorMessage();

                message.AddString("Port bound to connection '" + connection + "' in use-statement of datapath '´");
                message.AddEntity(childDp);
                message.AddString("' was not found. ");

                VerificationError error =
                  new VerificationError(
                    VerificationErrorType.ChildDatapathPortListError, 
                    message
                  );

                errors.Add(error);

                // dont report any more errors here ..
                return;
              }

              // check if signal is also assigned to from sfg's
              if (this.definers.Count > 0 && port is OutPort)
              {
                errors.Add(buildMultipleDefinersError());
                return; 
              }
              // this check is invalid because it may be wired up to another child ipblock/datapath 
              //else if (port is InPort && this.definers.Count == 0)
              //{
              //  errors.Add(buildNoDefinerError());
              //  return;
              //}
              else if (port is InPort)
              {
                // do nothing
              }
              else
              {
                if (!(port is OutPort))
                {
                  ErrorMessage message = new ErrorMessage();
                  message.AddString("Expected out port at position " + i + " in port list of use datapath statement for '"); 
                  message.AddEntity(childDp);
                  message.AddString("'");
                 
                  VerificationError error = 
                    new VerificationError(VerificationErrorType.ChildDatapathPortListError, message);

                  errors.Add(error);
                  
                  return;
                }
                else
                {
                  this.outportDriver = port as OutPort;
                  return;
                }
              }
            }
          }
        }

        // check if it has any definers at all?
        if (definers.Count == 0)
        {
          errors.Add(buildNoDefinerError());
          return;
        }
      }
    }

    private VerificationError buildNoDefinerError()
    {
      ErrorMessage message = new ErrorMessage();
      message.AddString("Signal ");
      message.AddEntity(this);
      message.AddString(" at position ");
      message.AddPosition(this.startPosition);
      message.AddString(" has no definers");
      return new VerificationError(VerificationErrorType.UnspecifiedError, message);
    }

    private VerificationError buildMultipleDefinersError()
    {
      ErrorMessage message = new ErrorMessage();
      message.AddString("Signal ");
      message.AddEntity(this);
      message.AddString(" at position ");
      message.AddPosition(this.startPosition);
      message.AddString(" has multiple definers");
      return new VerificationError(VerificationErrorType.UnspecifiedError, message);
    }
  }
}
